/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.j.lemon.oidc.config;

import com.alibaba.fastjson.JSONObject;
import com.j.lemon.oidc.jose.Jwks;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.*;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

import java.time.Duration;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;

/**
 * @author Joe Grandja
 * @since 0.0.1
 */
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {

	@Bean
	@Order(Ordered.HIGHEST_PRECEDENCE)
	public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
		OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
		http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
				// Enable OpenID Connect 1.0
				.oidc(Customizer.withDefaults());

		// @formatter:off
		http
				.exceptionHandling(exceptions ->
						exceptions.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
				)
				.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);




		// @formatter:on
		return http.build();
	}

	// @formatter:off
	@Bean
	public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
//		RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
//				.clientId("7lv5q8st")
//				.clientSecret("yb6bcbk8jt4u3w2azlrgvo71f715zota")
//				.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
//				.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
//				.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
//				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
//				.redirectUri("http://www.baidu.com")
////				.redirectUri("http://127.0.0.1:8080/authorized")
//				.scope(OidcScopes.OPENID)
//				.scope(OidcScopes.PROFILE)
//				.scope(OidcScopes.EMAIL)
//				.scope(OidcScopes.PHONE)
//				.scope(OidcScopes.ADDRESS)
//				.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
//				.tokenSettings(TokenSettings.builder()
//						.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
//						.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
//						.accessTokenTimeToLive(Duration.ofSeconds(30 * 60))
//						.refreshTokenTimeToLive(Duration.ofSeconds(60 * 60))
//						.reuseRefreshTokens(true)
//						.build())
//				.build();

		// Save registered client in db as if in-memory
//		JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
//		registeredClientRepository.save(registeredClient);

		return new JdbcRegisteredClientRepository(jdbcTemplate);
	}
	// @formatter:on

	@Bean
	public OAuth2TokenCustomizer<JwtEncodingContext> jwtTokenCustomizer() {
		return context -> {
			Authentication principal = context.getPrincipal();
			OAuth2Authorization authorization = context.getAuthorization();
			Set<String> authorizedScopes = authorization.getAuthorizedScopes();
			OAuth2TokenType tokenType = context.getTokenType();
			if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {
				JwtClaimsSet.Builder claims = context.getClaims();
				claims.claim("iss","http://10.28.149.177");
				context.getClaims().claims(new Consumer<Map<String, Object>>() {
					@Override
					public void accept(Map<String, Object> stringObjectMap) {
						if(authorizedScopes.contains(OidcScopes.PROFILE)){
							stringObjectMap.put("name", "李俊君");
							stringObjectMap.put("given_name", "");//firstName
							stringObjectMap.put("family_name", "");//lastName
							stringObjectMap.put("middle_name", "");
							stringObjectMap.put("nickname", "super");//昵称
//							stringObjectMap.put("preferred_username", "lijunjun");
							stringObjectMap.put("profile", "");//个人主页
							stringObjectMap.put("picture", "");//图片
							stringObjectMap.put("website", "");//个人网站
							stringObjectMap.put("gender", "男");//
							stringObjectMap.put("birthdate", "1991-02-27");//出生日期
							stringObjectMap.put("zoneinfo", "");//时区
							stringObjectMap.put("locale", "");//地区
						}
						if(authorizedScopes.contains(OidcScopes.EMAIL)){
							stringObjectMap.put("email", "lijunjun7531@163.com");
							stringObjectMap.put("email_verified", true);
						}
						if(authorizedScopes.contains(OidcScopes.PHONE)){
							stringObjectMap.put("phone_number", "13333333333");
							stringObjectMap.put("phone_number_verified", true);
						}
						if(authorizedScopes.contains(OidcScopes.ADDRESS)){
							JSONObject jsonObject = new JSONObject();
							jsonObject.put("formatted","");
							jsonObject.put("street_address","");
							jsonObject.put("locality","");
							jsonObject.put("region","");
							jsonObject.put("postal_code","");
							jsonObject.put("country","");
							stringObjectMap.put("address", jsonObject);
						}

					}
				});
			}

		};
	}

	@Bean
	public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
		return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
	}

	@Bean
	public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
		return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
	}



	@Bean
	public JWKSource<SecurityContext> jwkSource() {
		RSAKey rsaKey = Jwks.generateRsa();
		JWKSet jwkSet = new JWKSet(rsaKey);
		return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
	}

	@Bean
	public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
		return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
	}

	@Bean
	public AuthorizationServerSettings authorizationServerSettings() {
		return AuthorizationServerSettings.builder().build();
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return NoOpPasswordEncoder.getInstance();
	}


}
